﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using GameLib.Utils;
using GameLib.Serialization;
using GameLib.Entities;

namespace GameLib.WorldMap
{
	public class Map
	{
		public Map()
		{
			Tiles = new MapTileCollection(this);

		}

		public void Init(int width, int height)
		{
			Tiles.Init(width, height);

			for (int x = 0; x < width; x++)
			{
				int shift = x % 2;
				//int max_y = (x%2==0?height:height-1);
				for (int y = 0; y < height - shift; y++)
				{
					Tiles[x, y] = new Tile(this, x, y * 2 + shift);
				}
			}
		}

		public int Width
		{
			get { return AlignedWidth; }
		}

		public int Height
		{
			get { return AlignedHeight * 2; }
		}

		public int AlignedWidth
		{
			get { return Tiles.Width; }
		}

		public int AlignedHeight
		{
			get { return Tiles.Height; }
		}

		public int GetColumnHeight(int x)
		{
			return x % 2 == 0 ? Height : Height - 1;
		}

		public Tile GetTile(int x, int y)
		{
			GameVector alignedCoord = ToAlignedCoordinate(x, y);
			return GetTileAligned(alignedCoord.X, alignedCoord.Y);
		}

		public Tile GetTile(GameVector loc)
		{
			return GetTile(loc.X, loc.Y);
		}

		public Tile GetTileAligned(int x, int y)
		{
			if (x < 0 || x >= AlignedWidth || y < 0 || y >= AlignedHeight)
				return null;

			return Tiles[x, y];		
		}

		public static GameVector ToAlignedCoordinate(int x, int y)
		{
			return new GameVector(x, y / 2);
		}

		public static GameVector ToHexCoordinate(int x, int y)
		{
			return new GameVector(x, y * 2 + x % 2);
		}

		public static void RasterizeCircle(GameVector center, int radius, out GameVector[] tileCoords)
		{
			if (radius <= 0)
				throw new Exception(string.Format("cannot rasterize {0} sized circle", radius));

			int diameter = radius * 2 - 1;
			int total = 3 * radius * (radius - 1) + 1;
			tileCoords = new GameVector[total];
			int i = 0;

			for (int x = center.X - (radius - 1); x <= center.X + (radius - 1); x++)
			{
				int offset = Math.Abs(x - center.X);
				int halfHeight = ((diameter - offset) * 2 - 1) / 2;

				for (int y = center.Y - halfHeight; y <= center.Y + halfHeight; y += 2)
				{
					tileCoords[i++] = new GameVector(x, y);
				}
			}
		}

		public void AddRiver(River river)
		{
			if (!river.IsInitialized)
			{
				m_Rivers.Add(river);
				river.Initialize(this);

				if (RiverEmerged != null)
					RiverEmerged(river);
			}
		}

		public IEnumerable<River> Rivers
		{
			get { return m_Rivers; }
		}

		public IEnumerable<GameVector> GetVertexNeighbours(GameVector vertex)
		{
			if (vertex.X < 0 || vertex.X >= Width || vertex.Y < 0 || vertex.Y >= Height)
				yield break;


			if (vertex.Y > 0)
				yield return new GameVector(vertex.X, vertex.Y - 1);

			if (vertex.Y < Height - 1)
				yield return new GameVector(vertex.X, vertex.Y + 1);

			if (vertex.X % 2 == 0 ^ vertex.Y % 2 == 0)
			{
				if (vertex.X > 0)
					yield return new GameVector(vertex.X - 1, vertex.Y);
			}
			else
			{
				if (vertex.X < Width - 1)
					yield return new GameVector(vertex.X + 1, vertex.Y);
			}
		}

		/*
		public static bool IsNeighbourVertices(GameVector thisVert, GameVector thatVert)
		{
			if (thisVert.X == thatVert.X)
			{
				return (thisVert.Y == thatVert.Y + 1) || (thisVert.Y == thatVert.Y - 1);
			}
			else if (thisVert.Y == thatVert.Y)
			{
				if (thisVert.X % 2 == 0 ^ thisVert.Y % 2 == 0)
					return thatVert.X == thisVert.X - 1;
				else
					return thatVert.X == thisVert.X + 1;
			}
			else
			{
				return false;
			}
		}
		*/
		/*
		public void GetEdgeConnectedTiles(GameVector vertex0, GameVector vertex1, out Tile tile0, out Tile tile1)
		{
			tile0 = null;
			tile1 = null;

			if (!IsNeighbourVertices(vertex0, vertex1))
				return;

			if (vertex0.Y == vertex1.Y)
			{
				int x = Math.Min(vertex0.X, vertex1.X);
				int y = vertex0.Y / 2;
				tile0 = GetTile(x, y - 1);
				tile1 = GetTile(x, y);
			}
			else
			{
				int x = vertex0.X;
				int y = Math.Min(vertex0.Y, vertex1.Y);

				if (x % 2 == 0)
				{
					tile0 = GetTile(x - 1, (y + 1) / 2 - 1);
					tile1 = GetTile(x, y / 2);
				}
				else
				{
					tile0 = GetTile(x - 1, y / 2);
					tile1 = GetTile(x, (y + 1) / 2 - 1);
				}				
			}
		}
		*/


		//private int m_Width;
		//private int m_Height;

		[XmlDataProperty()]
		private MapTileCollection Tiles
		{
			get;
			set;
		}
		private RiverCollection m_Rivers = new RiverCollection();

		[XmlDataProperty()]
		public List<MapRefImageData> ReferenceImageData
		{
			get { return _ReferenceImageData; }
		}
		private List<MapRefImageData> _ReferenceImageData = new List<MapRefImageData>();

		[XmlDataProperty()]
		public ICollection<Settlement> Settlements
		{
			get { return _Settlements; }
		}
		public List<Settlement> _Settlements = new List<Settlement>();

		#region Events

		public event RiverEmergedEventHandler RiverEmerged;

		#endregion

	}

	public class MapTileCollection : TileCollection<Tile>
	{
		public MapTileCollection(Map map, int width, int height)
            : base(width, height)
        {
			Map = map;
        }

		public MapTileCollection(Map map)
			: this(map, 0, 0)
		{

		}

		public Map Map
		{
			get;
			private set;
		}
	}

	public class RiverCollection : List<River>
	{

	}

	public delegate void RiverEmergedEventHandler(River newRiver);

	public struct MapRefImageData
	{
		public string ImagePath;
		public double X;
		public double Y;
		public double Scale;
		public double Opacity;

		public MapRefImageData(string imagePath)
		{
			ImagePath = imagePath;
			Scale = 1.0;
			Opacity = 0.5;
			X = 0;
			Y = 0;
		}
	}
}
